<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Libs\Plugins\Aws;

use Exception;
use Aws\Credentials\Credentials;
use Aws\Ses\SesClient;
use PHPMailer\PHPMailer\PHPMailer;
use Srv\Libs\Common\CommJson;
use Srv\Libs\Common\CommValid;
use Srv\Libs\Frame\Logs;

class AwsEmail
{
    private string $apiKey                  = '';           // apiKey
    private string $secretKey               = '';           // secretKey
    private string $logPath                 = '';           // logPath
    private string $version                 = 'latest';     // version
    private string $region                  = 'us-east-1';  // region

    /**
     * @param string $apiKey
     * @param string $secretKey
     * @param string $logPath
     * __construct
     */
    public function __construct(string $apiKey, string $secretKey, string $logPath)
    {
        $this->apiKey       = $apiKey;
        $this->secretKey    = $secretKey;
        $this->logPath      = $logPath;
    }

    /**
     * @param array $toList
     * @param string $from
     * @param string $fromName
     * @param string $subject
     * @param string $htmlBody
     * @return bool
     * 发送Html邮件
     */
    public function sendMailHtml(array $toList, string $from, string $fromName, string $subject, string $htmlBody):bool
    {
        if(count($toList) < 1 || !CommValid::isEmail($from) || strlen($subject) < 1 || strlen($htmlBody) < 1) return false;
        $toEmailList        = [];
        foreach($toList as $to){
            if(!is_string($to) || !CommValid::isEmail($to)) continue;
            $toEmailList[]  = $to;
        }
        if(count($toEmailList) < 1) return false;
        $SesClient          = new SesClient([
            'version'       => $this->version,
            'region'        => $this->region,
            'credentials'   => new Credentials($this->apiKey, $this->secretKey),
        ]);
        if(strlen($fromName) > 0){
            try{
                $PHPMailer              = new PHPMailer();
                $PHPMailer->XMailer     = $fromName;
                $PHPMailer->CharSet     = PHPMailer::CHARSET_UTF8;
                $PHPMailer->ContentType = PHPMailer::CONTENT_TYPE_TEXT_HTML;
                $PHPMailer->setFrom($from, $fromName);
                $PHPMailer->addReplyTo($from, $fromName);
                foreach ($toEmailList as $to) $PHPMailer->addAddress($to);
                $PHPMailer->Subject     = $subject;
                $PHPMailer->Body        = $htmlBody;
                if(!$PHPMailer->preSend()){
                    return false;
                }
                $emailData          = [
                    'Destinations'   => $toEmailList,
                    'Source'        => $from,
                    'RawMessage'    => ['Data' => $PHPMailer->getSentMIMEMessage()],
                ];
                $result     = $SesClient->sendRawEmail($emailData); // docs: https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-email-2010-12-01.html#sendrawemail
                $resData    = $result->toArray();
                unset($emailData['Message'], $emailData['RawMessage'], $emailData['ReplyToAddresses'], $emailData['Source']);
                if(isset($resData['MessageId']) && isset($resData['@metadata']) && isset($resData['@metadata']['statusCode']) && $resData['@metadata']['statusCode'] === 200){
                    $this->writeLog(CommJson::encodeArray(['res' => 'OK', 'POST' => $emailData, 'RES' => CommJson::encode($resData)]), __LINE__, __FILE__);
                    return true;
                }
                $this->writeLog(CommJson::encodeArray(['res' => 'FAIL', 'POST' => $emailData, 'RES' => CommJson::encode($resData)]), __LINE__, __FILE__);
                return false;
            }catch (Exception $Exception){
                $this->writeLog(CommJson::encodeArray(['res' => 'FAIL', 'POST' => ['Destinations' => $toEmailList], 'RES' => $Exception->getMessage()]), __LINE__, __FILE__);
                return false;
            }
        }else{
            try{
                $emailData          = [
                    'Destination'       => ['ToAddresses' => $toEmailList],
                    'ReplyToAddresses'  => [$from],
                    'Source'            => $from,
                    'Message'           => [
                        'Body'      => ['Html' => ['Charset' => 'UTF-8', 'Data' => $htmlBody]],
                        'Subject'   => ['Charset' => 'UTF-8', 'Data' => $subject],
                    ],
                ];
                $result     = $SesClient->sendEmail($emailData);    // docs: https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-email-2010-12-01.html#sendemail
                $resData    = $result->toArray();
                unset($emailData['Message'], $emailData['RawMessage'], $emailData['ReplyToAddresses'], $emailData['Source']);
                if(isset($resData['MessageId']) && isset($resData['@metadata']) && isset($resData['@metadata']['statusCode']) && $resData['@metadata']['statusCode'] === 200){
                    $this->writeLog(CommJson::encodeArray(['res' => 'OK', 'POST' => $emailData, 'RES' => CommJson::encode($resData)]), __LINE__, __FILE__);
                    return true;
                }
                $this->writeLog(CommJson::encodeArray(['res' => 'FAIL', 'POST' => $emailData, 'RES' => CommJson::encode($resData)]), __LINE__, __FILE__);
                return false;
            }catch (Exception $Exception){
                $this->writeLog(CommJson::encodeArray(['res' => 'FAIL', 'POST' => ['Destination' => ['ToAddresses' => $toEmailList]], 'RES' => $Exception->getMessage()]), __LINE__, __FILE__);
                return false;
            }
        }
    }

    /**
     * @param array $toList
     * @param string $from
     * @param string $fromName
     * @param string $subject
     * @param string $content
     * @return bool
     * 发送文本邮件
     */
    public function sendMailText(array $toList, string $from, string $fromName, string $subject, string $content):bool
    {
        if(count($toList) < 1 || !CommValid::isEmail($from) || strlen($subject) < 1 || strlen($content) < 1) return false;
        $toEmailList        = [];
        foreach($toList as $to){
            if(!is_string($to) || !CommValid::isEmail($to)) continue;
            $toEmailList[]  = $to;
        }
        if(count($toEmailList) < 1) return false;
        $SesClient          = new SesClient([
            'version'       => $this->version,
            'region'        => $this->region,
            'credentials'   => new Credentials($this->apiKey, $this->secretKey),
        ]);
        if(strlen($fromName) > 0){
            try{
                $PHPMailer              = new PHPMailer();
                $PHPMailer->XMailer     = $fromName;
                $PHPMailer->CharSet     = PHPMailer::CHARSET_UTF8;
                $PHPMailer->ContentType = PHPMailer::CONTENT_TYPE_PLAINTEXT;
                $PHPMailer->setFrom($from, $fromName);
                $PHPMailer->addReplyTo($from, $fromName);
                foreach ($toEmailList as $to) $PHPMailer->addAddress($to);
                $PHPMailer->Subject     = $subject;
                $PHPMailer->AltBody     = $content;
                if(!$PHPMailer->preSend()){
                    return false;
                }
                $emailData          = [
                    'Destinations'   => $toEmailList,
                    'Source'        => $from,
                    'RawMessage'    => ['Data' => $PHPMailer->getSentMIMEMessage()],
                ];
                $result     = $SesClient->sendRawEmail($emailData);
                $resData    = $result->toArray();
                unset($emailData['Message'], $emailData['RawMessage'], $emailData['ReplyToAddresses'], $emailData['Source']);
                if(isset($resData['MessageId']) && isset($resData['@metadata']) && isset($resData['@metadata']['statusCode']) && $resData['@metadata']['statusCode'] === 200){
                    $this->writeLog(CommJson::encodeArray(['res' => 'OK', 'POST' => $emailData, 'RES' => CommJson::encode($resData)]), __LINE__, __FILE__);
                    return true;
                }
                $this->writeLog(CommJson::encodeArray(['res' => 'FAIL', 'POST' => $emailData, 'RES' => CommJson::encode($resData)]), __LINE__, __FILE__);
                return false;
            }catch (Exception $Exception){
                $this->writeLog(CommJson::encodeArray(['res' => 'FAIL', 'POST' => ['Destinations' => $toEmailList], 'RES' => $Exception->getMessage()]), __LINE__, __FILE__);
                return false;
            }
        }else{
            try{
                $emailData          = [
                    'Destination'       => ['ToAddresses' => $toEmailList],
                    'ReplyToAddresses'  => [$from],
                    'Source'            => $from,
                    'Message'           => [
                        'Body'      => ['Text' => ['Charset' => 'UTF-8', 'Data' => $content]],
                        'Subject'   => ['Charset' => 'UTF-8', 'Data' => $subject],
                    ],
                ];
                $result     = $SesClient->sendEmail($emailData);
                $resData    = $result->toArray();
                unset($emailData['Message'], $emailData['RawMessage'], $emailData['ReplyToAddresses'], $emailData['Source']);
                if(isset($resData['MessageId']) && isset($resData['@metadata']) && isset($resData['@metadata']['statusCode']) && $resData['@metadata']['statusCode'] === 200){
                    $this->writeLog(CommJson::encodeArray(['res' => 'OK', 'POST' => $emailData, 'RES' => CommJson::encode($resData)]), __LINE__, __FILE__);
                    return true;
                }
                $this->writeLog(CommJson::encodeArray(['res' => 'FAIL', 'POST' => $emailData, 'RES' => CommJson::encode($resData)]), __LINE__, __FILE__);
                return false;
            }catch (Exception $Exception){
                $this->writeLog(CommJson::encodeArray(['res' => 'FAIL', 'POST' => ['Destination' => ['ToAddresses' => $toEmailList]], 'RES' => $Exception->getMessage()]), __LINE__, __FILE__);
                return false;
            }
        }
    }

    /**
     * @param string $logString
     * @param int $fileLine
     * @param string $filePath
     * @return void
     * writeLog
     */
    private function writeLog(string $logString, int $fileLine, string $filePath):void
    {
        if(strlen($this->logPath) > 0) Logs::info($logString, $fileLine, $filePath, $this->logPath);
    }
}
